package wsp.dailymarket.auth;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Resource;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.validator.ValidatorException;
import javax.sql.DataSource;
import wsp.dailymarket.obj.UserProfileBean;

@ManagedBean(name = "authenticationBean")
@SessionScoped
public class AuthenticationBean {

	// Authentication object
	private Authentication auth;
	// Signup vars
	private String signupEmail;
	private String signupPassword;
	private String signupConfirmPassword;
	private String signupFirstName;
	private String signupLastName;
	private String signupCompanyName;
	private int signupClientType;
	private UIComponent signupConfirmPasswordComponent;
	// Login vars
	private String loginEmail;
	private String loginPassword;
	private String loginDestination;
	// Profile
	private UserProfileBean profile;
	@Resource(name = "jdbc/dmderby")
	private DataSource ds;
	private ResourceBundle bundle;

	/** Creates a new instance of AuthenticationBean */
	public AuthenticationBean() {
		this.signupEmail = "";
		this.signupPassword = "";
		this.signupConfirmPassword = "";
		this.signupFirstName = "";
		this.signupLastName = "";
		this.signupCompanyName = "";
		this.loginEmail = "";
		this.loginPassword = "";
		this.loginDestination = "";
		this.signupClientType = UserProfileBean.CLIENT_TYPE_CUSTOMER;
		this.signupConfirmPasswordComponent = null;
		this.auth = Authentication.createEmpty();
		this.profile = new UserProfileBean();

		UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
		Locale locale = viewRoot.getLocale();
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		bundle = ResourceBundle.getBundle("wsp.dailymarket.messages", locale, loader);

	}

	/**
	 * Validate an email address
	 * @param fc FacesContext object
	 * @param uic UIComonent object
	 * @param o Object to be validated
	 * @throws ValidatorException
	 */
	public void validateEmail(FacesContext fc, UIComponent uic, Object o) throws ValidatorException {

		String enteredEmail = (String) o;
		//Set the email pattern string
		Pattern p = Pattern.compile(".+@.+\\.[a-z]+");
		//Match the given string with the pattern
		Matcher m = p.matcher(enteredEmail);
		//Check whether match is found
		boolean matchFound = m.matches();
		if (!matchFound) {
			FacesMessage message = new FacesMessage();
			message.setDetail(bundle.getString("email_invalid_detail"));
			message.setSummary(bundle.getString("email_invalid_summary"));
			message.setSeverity(FacesMessage.SEVERITY_ERROR);
			throw new ValidatorException(message);
		}

		// Now check if the email is already registered
		Connection conn = null;
		try {
			try {
				conn = ds.getConnection();
				Statement stmt = conn.createStatement();
				ResultSet rs = stmt.executeQuery("SELECT client_id FROM APP.CLIENTS WHERE client_id = '" + enteredEmail + "'");
				if (rs.next()) {
					FacesMessage message = new FacesMessage();
					message.setDetail(bundle.getString("email_registered_detail"));
					message.setSummary(bundle.getString("email_registered_summary"));
					message.setSeverity(FacesMessage.SEVERITY_ERROR);
					throw new ValidatorException(message);
				}
			} finally {
				if (conn != null) {
					conn.close();
				}
			}
		} catch (SQLException ex) {
			FacesMessage message = new FacesMessage();
			message.setDetail(ex.getMessage());
			message.setSummary(bundle.getString("database_error"));
			message.setSeverity(FacesMessage.SEVERITY_FATAL);
			throw new ValidatorException(message);
		} catch (NullPointerException ex) {
			ex.printStackTrace(System.err);
			FacesMessage message = new FacesMessage();
			message.setDetail(ex.getMessage());
			message.setSummary(bundle.getString("database_error"));
			message.setSeverity(FacesMessage.SEVERITY_FATAL);
			throw new ValidatorException(message);
		}
	}

	/**
	 * Run the sign up operation
	 * @return Next target location
	 */
	public String doFinishSignup() {

		// Validate passwords
		if (!this.signupPassword.equals(this.signupConfirmPassword)) {
			// Passwords did not match
			FacesMessage message = new FacesMessage();
			message.setSeverity(FacesMessage.SEVERITY_ERROR);
			message.setSummary(bundle == null ? "Bundle not found!" : bundle.getString("confirmpassword_nomatch"));
			FacesContext.getCurrentInstance().addMessage(this.signupConfirmPasswordComponent.getClientId(), message);
			return "signup";
		}

		// Add user to database
		Connection conn = null;
		try {
			try {
				conn = ds.getConnection();
				Statement stmt = conn.createStatement();
				// SQL Statement to insert a customer
				String sql = "INSERT INTO app.clients (client_id, "
						+ "password, firstname, lastname, companyname, client_type)"
						+ " VALUES ('"
						+ this.signupEmail
						+ "', '"
						+ this.signupPassword
						+ "', "
						+ (this.signupFirstName.equals("") ? "NULL" : "'" + this.signupFirstName + "'")
						+ ", "
						+ (this.signupLastName.equals("") ? "NULL" : "'" + this.signupLastName + "'")
						+ ", "
						+ (this.signupCompanyName.equals("") ? "NULL" : "'" + this.signupCompanyName + "'")
						+ ", "
						+ String.valueOf(this.signupClientType)
						+ ")";
				if (stmt.executeUpdate(sql) != 1) {
					FacesMessage message = new FacesMessage();
					message.setSeverity(FacesMessage.SEVERITY_ERROR);
					message.setSummary(bundle == null ? "Bundle not found!" : bundle.getString("database_error"));
					FacesContext.getCurrentInstance().addMessage(null, message);
				}
			} finally {
				if (conn != null) {
					conn.close();
				}
			}
		} catch (SQLException ex) {
			FacesMessage message = new FacesMessage();
			message.setSeverity(FacesMessage.SEVERITY_FATAL);
			message.setSummary(bundle == null ? "Bundle not found!" : bundle.getString("database_error") + ": " + ex.getMessage());
			FacesContext.getCurrentInstance().addMessage(null, message);
			return "signup";
		}

		// Make the auth object valid
		auth = Authentication.getAuthorization(ds, signupEmail, signupPassword);

		try {
			// Make profile valid
			profile.loadProfile(ds, auth.getUserID(), auth.getClientType());
		} catch (SQLException ex) {
			FacesMessage message = new FacesMessage();
			message.setSeverity(FacesMessage.SEVERITY_FATAL);
			message.setSummary(bundle == null ? "Bundle not found!" : bundle.getString("database_error") + ": " + ex.getMessage());
			FacesContext.getCurrentInstance().addMessage(null, message);
			return "signup";
		} catch (IllegalArgumentException ex) {
			FacesMessage message = new FacesMessage();
			message.setSeverity(FacesMessage.SEVERITY_FATAL);
			message.setSummary(bundle == null ? "Bundle not found!" : bundle.getString("database_error") + ": " + ex.getMessage());
			FacesContext.getCurrentInstance().addMessage(null, message);
			return "signup";
		}

		if (this.loginDestination.length() > 0) {
			try {
				return "success-" + this.loginDestination;
			} finally {
				this.loginDestination = "";
			}
		} else {
			return "success-" + profile.getClientTypeDisplay();
		}
	}

	public String doLogin() {

		auth = Authentication.getAuthorization(ds, this.loginEmail, this.loginPassword);
		if (!auth.isAuthorized()) {
			FacesMessage message = new FacesMessage();
			message.setSeverity(FacesMessage.SEVERITY_ERROR);
			message.setSummary(auth.getStatusMessage());
			FacesContext.getCurrentInstance().addMessage(null, message);
			return "login";
		}

		try {
			// Make profile valid
			profile.loadProfile(ds, auth.getUserID(), auth.getClientType());
		} catch (SQLException ex) {
			FacesMessage message = new FacesMessage();
			message.setSeverity(FacesMessage.SEVERITY_FATAL);
			message.setSummary(bundle == null ? "Bundle not found!" : bundle.getString("database_error") + ": " + ex.getMessage());
			FacesContext.getCurrentInstance().addMessage(null, message);
			return "login";
		} catch (IllegalArgumentException ex) {
			FacesMessage message = new FacesMessage();
			message.setSeverity(FacesMessage.SEVERITY_FATAL);
			message.setSummary(bundle == null ? "Bundle not found!" : bundle.getString("database_error") + ": " + ex.getMessage());
			FacesContext.getCurrentInstance().addMessage(null, message);
			return "login";
		}
		if (this.loginDestination.length() > 0) {
			try {
				return "success-" + this.loginDestination;
			} finally {
				this.loginDestination = "";
			}
		} else {
			return "success-" + profile.getClientTypeDisplay();
		}
	}

	public String doLogout() {
		auth = Authentication.createEmpty();
		profile = new UserProfileBean();
		return "logged-out";
	}

	public String saveProfile() {
		try {
			profile.saveProfile(ds);
			FacesMessage message = new FacesMessage();
			message.setSeverity(FacesMessage.SEVERITY_INFO);
			message.setSummary("Saved!");
			FacesContext.getCurrentInstance().addMessage(null, message);
		} catch (SQLException ex) {
			FacesMessage message = new FacesMessage();
			message.setSeverity(FacesMessage.SEVERITY_FATAL);
			message.setSummary(bundle == null ? "Bundle not found!" : bundle.getString("database_error"));
			FacesContext.getCurrentInstance().addMessage(null, message);
		} catch (Exception ex) {
			FacesMessage message = new FacesMessage();
			message.setSeverity(FacesMessage.SEVERITY_FATAL);
			message.setSummary(bundle == null ? "Bundle not found!" : bundle.getString("database_error"));
			FacesContext.getCurrentInstance().addMessage(null, message);
		}
		return this.getProfileOutcome();
	}

	public String doSignupCompany() {
		this.signupClientType = UserProfileBean.CLIENT_TYPE_PARTNER;
		return "signup";
	}

	public String doSignupCustomer() {
		this.signupClientType = UserProfileBean.CLIENT_TYPE_CUSTOMER;
		return "signup";
	}

	public String getProfileOutcome() {
		if (this.profile == null) {
			return "login";
		}
		switch (this.profile.getClientType()) {
			case UserProfileBean.CLIENT_TYPE_ADMIN:
				return "admin-profile";
			case UserProfileBean.CLIENT_TYPE_PARTNER:
				return "partner-profile";
			case UserProfileBean.CLIENT_TYPE_CUSTOMER:
			default:
				return "profile";
		}
	}

	public String getProfileView() {
		if (this.profile == null) {
			return "/login.xhtml";
		}
		switch (this.profile.getClientType()) {
			case UserProfileBean.CLIENT_TYPE_ADMIN:
				return "/admin/profile.xhtml";
			case UserProfileBean.CLIENT_TYPE_PARTNER:
				return "/partner/profile.xhtml";
			case UserProfileBean.CLIENT_TYPE_CUSTOMER:
			default:
				return "/customer/profile.xhtml";
		}
	}

	// Property accesses
	public void setSignupEmail(String newValue) {
		this.signupEmail = newValue;
	}

	public String getSignupEmail() {
		return this.signupEmail;
	}

	public void setSignupFirstName(String newValue) {
		this.signupFirstName = newValue;
	}

	public String getSignupFirstName() {
		return this.signupFirstName;
	}

	public void setSignupLastName(String newValue) {
		this.signupLastName = newValue;
	}

	public String getSignupLastName() {
		return this.signupLastName;
	}

	public void setSignupPassword(String newValue) {
		this.signupPassword = newValue;
	}

	public String getSignupPassword() {
		return this.signupPassword;
	}

	public void setSignupConfirmPassword(String newValue) {
		this.signupConfirmPassword = newValue;
	}

	public String getSignupConfirmPassword() {
		return this.signupConfirmPassword;
	}

	public void setSignupCompanyName(String newValue) {
		this.signupCompanyName = newValue;
	}

	public String getSignupCompanyName() {
		return this.signupCompanyName;
	}

	public String getSignupClientTypeString() {
		return this.signupClientType == UserProfileBean.CLIENT_TYPE_PARTNER ? "Partner" : "Customer";
	}

	public void setSignupConfirmPasswordComponent(UIComponent newComp) {
		this.signupConfirmPasswordComponent = newComp;
	}

	public UIComponent getSignupConfirmPasswordComponent() {
		return this.signupConfirmPasswordComponent;
	}

	public boolean isSignupEmailDefined() {
		return this.signupEmail != null && !this.signupEmail.equals("");
	}

	public void setLoginEmail(String newValue) {
		this.loginEmail = newValue;
	}

	public String getLoginEmail() {
		return this.loginEmail;
	}

	public void setLoginPassword(String newValue) {
		this.loginPassword = newValue;
	}

	public String getLoginPassword() {
		return this.loginPassword;
	}

	public void setLoginDestination(String newValue) {
		this.loginDestination = newValue;
	}

	public String getLoginDestination() {
		return this.loginDestination;
	}

	public Authentication getAuthentication() {
		return this.auth;
	}

	public UserProfileBean getUserProfile() {
		return this.profile;
	}

	public ResourceBundle getMessageBundle() {
		return this.bundle;
	}
}
